package com.torry.edss.integration.facade.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.torry.boot.common.model.result.Result;
import com.torry.boot.common.util.Preconditions;
import com.torry.edss.application.cache.TransactionDataCache;
import com.torry.edss.application.exception.ReplayExceptionEnum;
import com.torry.edss.domain.accessor.CompanyAccessor;
import com.torry.edss.domain.model.Company;
import com.torry.edss.domain.valueobject.Bourse;
import com.torry.edss.domain.valueobject.MarketPlate;
import com.torry.edss.infras.util.DateUtil;
import com.torry.edss.infras.util.NumberUtils;
import com.torry.edss.integration.facade.RemoteApiFacade;
import com.torry.edss.integration.facade.enums.Direction;
import com.torry.edss.integration.facade.result.*;
import com.torry.edss.integration.feign.cls.ClsFeignClient;
import com.torry.edss.integration.feign.cls.response.ClsRealTimeEmotionDataResponse;
import com.torry.edss.integration.feign.cls.response.ClsRealTimeMarketDataResponse;
import com.torry.edss.integration.feign.eastmoney.EastMoneyPush2ExFeignClient;
import com.torry.edss.integration.feign.eastmoney.EastMoneyPush2HisFeignClient;
import com.torry.edss.integration.feign.eastmoney.EastMoneyWebFeignClient;
import com.torry.edss.integration.feign.eastmoney.response.*;
import com.torry.edss.integration.feign.mairui.MairuiFeignClient;
import com.torry.edss.integration.feign.mairui.response.MairuiAllStocksResponse;
import com.torry.edss.integration.feign.tushare.TuShareFeignClient;
import com.torry.edss.integration.feign.tushare.request.TushareFetchDailyLineRequest;
import com.torry.edss.integration.feign.tushare.request.TushareRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.error.Mark;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Service
@Slf4j
public class RemoteApiFacadeImpl implements RemoteApiFacade {

    @Resource
    private ClsFeignClient clsFeignClient;
    @Resource
    private MairuiFeignClient mairuiFeignClient;
    @Resource
    private EastMoneyPush2HisFeignClient eastMoneyPush2HisFeignClient;
    @Resource
    private EastMoneyPush2ExFeignClient eastMoneyPush2ExFeignClient;
    @Resource
    private EastMoneyWebFeignClient eastMoneyWebFeignClient;

    @Override
    public List<FetchAllStocksResult> fetchAllStocks() {
        List<MairuiAllStocksResponse> responses = mairuiFeignClient.fetchAllStocks();
        return responses.stream().map(e -> {
            FetchAllStocksResult result = new FetchAllStocksResult();
            result.setName(e.getName().replaceAll("\\s+", "").replaceAll("Ａ", "A"));
            result.setCode(e.getCode().replaceAll(".SZ", "").replaceAll(".SH", ""));
            result.setBourse(Bourse.parse(e.getJys().toLowerCase()));
            return result;
        }).collect(Collectors.toList());
    }

    @Override
    public List<FetchDailyLineResult> fetchRealTimeDailyLines(String code, Integer limit) {
//        log.info("fetchRealTimeDailyLines, code: {}", code);
        String prefix = "0.";
        if (!code.startsWith("0") & !code.startsWith("3")) {
            prefix = "1.";
        }
        String fullCode = prefix + code;
        EMDailyLineResponse response = eastMoneyPush2HisFeignClient.fetchRealTimeDailyLines(fullCode, limit);
        if (response == null || response.getData() == null) return Collections.emptyList();
        EMDailyLineResponse.StockData data = response.getData();
        List<String> klines = data.getKlines();
        return klines.stream().map(kline -> {
            String[] split = kline.split(",");

            FetchDailyLineResult result = new FetchDailyLineResult();
            result.setDate(split[0]);
            result.setOpen(Double.parseDouble(split[1]));
            result.setPrice(Double.parseDouble(split[2]));
            result.setHigh(Double.parseDouble(split[3]));
            result.setLow(Double.parseDouble(split[4]));
            result.setVolume(Double.parseDouble(split[5]));
            result.setAmount(Double.parseDouble(split[6]));
            result.setAmplitude(Double.parseDouble(split[7]));
            result.setUpPercentage(Double.parseDouble(split[8]));
            result.setUpValue(Double.parseDouble(split[9]));
            result.setTurnoverRate(Double.parseDouble(split[10]));
            return result;

        }).collect(Collectors.toList());
    }

    @Override
    public FetchDailyLineResult fetchCurrentDayLine(String code) {
        List<FetchDailyLineResult> results = fetchAllDailyLines(code);
        return results.get(results.size() - 1);
    }

    @Override
    public List<FetchDailyLineResult> fetchAllDailyLines(String code) {
        return TransactionDataCache.allKinesCache.getUnchecked(code);
//        return fetchRealTimeDailyLines(code, 99999);
    }

    @Override
    public LocalDate fetchStockLDate(String code) {
        EMStockDetailInfoResponse response = eastMoneyWebFeignClient.fetchStockDetailInfo(code);
        if (response == null || !response.getSuccess()) {
            return null;
        }
        List<EMStockDetailInfoResponse.CompanyBasicInfo> data = response.getResult().getData();
        EMStockDetailInfoResponse.CompanyBasicInfo detailInfo = data.get(0);
        String listingDate = detailInfo.getListingDate();
        if (StringUtils.isNotBlank(listingDate)) {
            log.info("get company`s ldate : {}, {}", code, listingDate);
            LocalDate date = DateUtil.parseDate(listingDate, "yyyy-MM-dd HH:mm:ss");
            return date;
        }
        return null;
    }

    @Override
    public List<FetchLimitUpResult> fetchLimitUps(String date) {
        String dateParam = date.replaceAll("-", "");
        EMLimitUpPoolResponse response = eastMoneyPush2ExFeignClient.fetchLimitUps(dateParam);
        List<EMLimitUpPoolResponse.LimitUpStock> list = response.getData().getPool();
        list.forEach(e -> {
            e.setCode(e.getCode().replaceAll("sz", "").replaceAll("sh", ""));
        });
        list = list.stream().filter(e -> MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.HSZB || MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.KCB_CYB).collect(Collectors.toList());

        return list.stream().map(e -> {
            FetchLimitUpResult result = new FetchLimitUpResult();
            result.setCode(e.getCode());
            result.setMarketType(e.getMarketType());
            result.setName(e.getName());
            result.setPrice(turnLiToYuan(e.getPrice()));
            result.setUpPercentage(NumberUtils.format(e.getChangePercent()));
            result.setAmount(e.getAmount());
            result.setLtsz(e.getLtsz());
            result.setZsz(e.getTotalMarketValue());
            result.setTurnoverRate(NumberUtils.format(e.getTurnoverRate()));
            result.setSealAmount(e.getSealAmount());
            result.setContinuousCount(e.getLbc());
            result.setFirstLimitUpTime(formatterTime(e.getFirstLimitUpTime()));
            result.setLastLimitUpTime(formatterTime(e.getLastLimitUpTime()));
            result.setBombCount(e.getZbc());
            result.setIndustry(e.getIndustry());

            EMLimitUpPoolResponse.LimitUpStats stat = e.getStat();
            result.setStat(stat.getDays() + "/" + stat.getLimitCount());

            return result;
        }).collect(Collectors.toList());
    }

    @Override
    public List<FetchLimitDownResult> fetchLimitDowns(String date) {
        String dateParam = date.replaceAll("-", "");
        EMLimitDownPoolResponse response = eastMoneyPush2ExFeignClient.fetchLimitDowns(dateParam);

        List<EMLimitDownPoolResponse.LimitDownStockInfo> list = response.getData().getPool();
        list.forEach(e -> {
            e.setCode(e.getCode().replaceAll("sz", "").replaceAll("sh", ""));
        });
        list = list.stream().filter(e ->
                MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.HSZB || MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.KCB_CYB)
                .collect(Collectors.toList());

        return list.stream().map(e -> {
            FetchLimitDownResult result = new FetchLimitDownResult();
            result.setCode(e.getCode());
            result.setMarketType(e.getMarketType());
            result.setName(e.getName());
            result.setPrice(turnLiToYuan(e.getPrice()));
            result.setUpPercentage(NumberUtils.format(e.getChangePercent()));
            result.setAmount(e.getAmount());
            result.setLtsz(e.getLtsz());
            result.setZsz(e.getTotalMarketValue());
            result.setPe(e.getPe());
            result.setTurnoverRate(NumberUtils.format(e.getTurnoverRate()));
            result.setSealAmount(e.getSealAmount());
            result.setLatestLimitDowntime(formatterTime(e.getLatestLimitDowntime()));
            result.setMinPriceAmount(e.getFba());
            result.setContinuousCount(e.getDays());
            result.setBombCount(e.getOpenCount());
            result.setIndustry(e.getIndustry());
            return result;
        }).collect(Collectors.toList());
    }

    @Override
    public List<FetchLimitBombResult> fetchLimitBombs(String date) {
        String dateParam = date.replaceAll("-", "");
        EMLimitBombPoolResponse response = eastMoneyPush2ExFeignClient.fetchLimitBombs(dateParam);
        List<EMLimitBombPoolResponse.LimitBombStockInfo> list = response.getData().getPool();
        list.forEach(e -> {
            e.setCode(e.getCode().replaceAll("sz", "").replaceAll("sh", ""));
        });
        list = list.stream().filter(e ->
                MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.HSZB || MarketPlate.getMarketPlate(e.getCode()) == MarketPlate.KCB_CYB)
                .collect(Collectors.toList());
        return list.stream().map(e -> {
            FetchLimitBombResult result = new FetchLimitBombResult();
            result.setCode(e.getCode());
            result.setMarketType(e.getMarketType());
            result.setName(e.getName());
            result.setPrice(turnLiToYuan(e.getPrice()));
            result.setLimitUpPrice(turnLiToYuan(e.getLimitUpPrice()));
            result.setUpPercentage(NumberUtils.format(e.getChangePercent()));
            result.setAmount(e.getAmount());
            result.setLtsz(e.getLtsz());
            result.setZsz(e.getTotalMarketValue());
            result.setTurnoverRate(NumberUtils.format(e.getTurnoverRate()));

            result.setFirstLimitTime(formatterTime(e.getFirstLimitTime()));
            result.setBombCount(e.getBombCount());
            result.setAmplitude(NumberUtils.format(e.getAmplitude()));
            result.setUpSpeed(NumberUtils.format(e.getZs()));

            result.setStat(e.getLimitUpStats().getDays() + "/" + e.getLimitUpStats().getLimitCount());
            result.setIndustry(e.getIndustry());

            return result;
        }).collect(Collectors.toList());
    }


    @Override
    public List<FetchFundFlowInfoResult> fetchFundFlowInfo() {
        EMIndexFundFlowResponse response = eastMoneyPush2HisFeignClient.fetchFundFlows();
        EMIndexFundFlowResponse.FundFlowData data = response.getData();
        List<String> klines = data.getKlines();
        return klines.stream().map(e -> {
            String[] split = e.split(",");
            FetchFundFlowInfoResult result = new FetchFundFlowInfoResult();
            result.setDate(split[0]);
            result.setTotalAmount(Double.valueOf(split[1]));
            result.setSmallAmount(Double.valueOf(split[2]));
            result.setMiddleAmount(Double.valueOf(split[3]));
            result.setLargeAmount(Double.valueOf(split[4]));
            result.setSuperLargeAmount(Double.valueOf(split[5]));
            result.setSmallProportion(Double.valueOf(split[6]));
            result.setMiddleProportion(Double.valueOf(split[7]));
            result.setLargeProportion(Double.valueOf(split[8]));
            result.setSuperLargeProportion(Double.valueOf(split[9]));
            result.setShExponent(Double.valueOf(split[10]));
            result.setShUpPercentage(Double.valueOf(split[11]));
            result.setSzExponent(Double.valueOf(split[12]));
            result.setSzUpPercentage(Double.valueOf(split[13]));

            return result;
        }).collect(Collectors.toList());
    }

    @Override
    public FetchFundFlowInfoResult fetchFundFlowInfo(String date) {
        List<FetchFundFlowInfoResult> fetchFundFlowInfoResults = this.fetchFundFlowInfo();
        return fetchFundFlowInfoResults.stream().filter(e -> e.getDate().equals(date)).findFirst().orElse(null);
    }

    private static String formatterTime(String time) {
        String formattedTime = String.format("%6s", time).replace(' ', '0');
        return formattedTime.substring(0, 2) + ":" +
                formattedTime.substring(2, 4) + ":" +
                formattedTime.substring(4, 6);
    }

    private static BigDecimal turnLiToYuan(Double li) {
        return NumberUtils.formatBigDecimal(li / 1000);
    }

    @Override
    public FetchRealTimeMarketDataResult fetchRealTimeMarketData() {
        Result<ClsRealTimeMarketDataResponse> response = clsFeignClient.fetchRealTimeMarketData();
        ClsRealTimeMarketDataResponse data = response.getData();

        List<FetchRealTimeMarketDataResult.IndexQuote> indexQuotes = data.getIndexQuote().stream().map(e -> {
            FetchRealTimeMarketDataResult.IndexQuote indexQuote = new FetchRealTimeMarketDataResult.IndexQuote();
            indexQuote.setCode(e.getSecuCode());
            indexQuote.setName(e.getSecuName());
            indexQuote.setPrice(e.getLastPx());
            indexQuote.setUpPercentage(NumberUtils.format(e.getChange() * 100));
            indexQuote.setUpValue(e.getChangePx());
            return indexQuote;
        }).collect(Collectors.toList());

        FetchRealTimeMarketDataResult result = new FetchRealTimeMarketDataResult();
        result.setIndexQuotes(indexQuotes);
        return result;
    }

    @Override
    public Double fetchRealTimeTotalAmount(Double previousDayTotalAmount) {
        Result<ClsRealTimeEmotionDataResponse> response = clsFeignClient.fetchRealTimeEmotionData();

        String changeValue = response.getData().getShszBalanceChange().replaceAll("亿", "").replace("+", "");
        if (changeValue.contains("万")) {
            changeValue = changeValue.replaceAll("万", "");
            double change = Double.parseDouble(changeValue) * 10000;
            return previousDayTotalAmount + change;
        }
        return previousDayTotalAmount + Double.parseDouble(changeValue);
    }

    @Override
    public FetchUpDownInfoResult fetchRealTimeUpDownInfo() {
        Result<ClsRealTimeEmotionDataResponse> response = clsFeignClient.fetchRealTimeEmotionData();
        ClsRealTimeEmotionDataResponse data = response.getData();
        FetchUpDownInfoResult result = new FetchUpDownInfoResult();
        result.setRiseNum(data.getUpDownDistribution().getRiseNum());
        result.setFallNum(data.getUpDownDistribution().getFallNum());
        result.setFlatNum(data.getUpDownDistribution().getFlatNum());

        return result;
    }

    @Override
    public FetchDailyLineResult fetchPreviousDayLine(String code, String currentDate) {
        checkCurrentDate(code, currentDate);
        List<FetchDailyLineResult> allDailyData = this.fetchAllDailyLines(code);
        int index = allDailyData.stream().map(FetchDailyLineResult::getDate).collect(Collectors.toList()).indexOf(currentDate);
        return index == 0 ? null : allDailyData.get(index - 1);
    }

    @Override
    public FetchDailyLineResult fetchNexDayLine(String code, String currentDate) {
        checkCurrentDate(code, currentDate);
        List<FetchDailyLineResult> allDailyData = this.fetchAllDailyLines(code);
        int index = allDailyData.stream().map(FetchDailyLineResult::getDate).collect(Collectors.toList()).indexOf(currentDate);
        return index == allDailyData.size() - 1 ? null : allDailyData.get(index + 1);
    }

    private void checkCurrentDate(String code, String currentDate) {
        FetchDailyLineResult result = fetchDayLine(code, currentDate, Direction.CURRENT);
        Preconditions.ofNotNull(result, ReplayExceptionEnum.CAN_NOT_FOUND_CURRENT_DAY_TRADE_INFO, code, currentDate);
    }

    @Override
    public FetchDailyLineResult fetchDayLine(String code, String date, Direction direction) {
        List<FetchDailyLineResult> kLines = this.fetchAllDailyLines(code);

        FetchDailyLineResult result = kLines.stream().filter(e -> e.getDate().equals(date)).findFirst().orElse(null);
        if (result != null) return result;
        if (direction != Direction.CURRENT) {
            LocalDate newDate = direction == Direction.EARLIER ? DateUtil.parseDate(date).minusDays(1) : DateUtil.parseDate(date).plusDays(1);
            return fetchDayLine(code, DateUtil.formatDate(newDate), direction);
        }
        return null;
    }

    @Resource
    private TuShareFeignClient tuShareFeignClient;
    @Resource
    private CompanyAccessor companyAccessor;

    @Override
    public List<FetchDailyLineResult> fetchHisDailyLines(String code, Integer limit) {
        List<FetchDailyLineResult> results = new ArrayList<>();
        if (MarketPlate.getMarketPlate(code) == MarketPlate.HSZB || MarketPlate.getMarketPlate(code) == MarketPlate.KCB_CYB) {
            TushareRequest<TushareFetchDailyLineRequest> request = new TushareRequest<>();
            request.setApiName("daily");

            TushareFetchDailyLineRequest param = new TushareFetchDailyLineRequest();
            Company company = companyAccessor.getByCode(code);
            param.setTsCode(code + "." + company.getBourse().getCode().toUpperCase());
            param.setOffset(0);
            param.setLimit(limit);
            request.setParams(param);
            Result<JSONObject> result = tuShareFeignClient.fetchData(request);
            JSONObject data = result.getData();
            JSONArray items = data.getJSONArray("items");


            for (int i = 0; i < items.size(); i++) {
                List list = items.getObject(i, List.class);
                FetchDailyLineResult result1 = new FetchDailyLineResult();
                LocalDate yyyymmdd = DateUtil.parseDate((String) list.get(1), "yyyyMMdd");
                result1.setDate(DateUtil.formatDate(yyyymmdd));
                result1.setOpen((Double) list.get(2));
                result1.setHigh((Double) list.get(3));
                result1.setLow((Double) list.get(4));
                result1.setPrice((Double) list.get(5));
                result1.setUpValue((Double) list.get(7));
                result1.setUpPercentage(NumberUtils.format((Double) list.get(8)));
                result1.setVolume((Double) list.get(9));
                if (Objects.nonNull(list.get(10))) {
                    result1.setAmount((Double) list.get(10) * 1000D);
                }
                result1.setCode(code);
                results.add(result1);

            }

        }

        return results;
    }

    @Override
    public List<FetchDailyLineResult> fetchHisDailyLines(String date) {
        TushareRequest<TushareFetchDailyLineRequest> request = new TushareRequest<>();
        request.setApiName("daily");

        TushareFetchDailyLineRequest param = new TushareFetchDailyLineRequest();
        param.setOffset(0);
        param.setLimit(6000);
        param.setTradeDate(DateUtil.formatDate(DateUtil.parseDate(date, "yyyy-MM-dd"), "yyyyMMdd"));
        request.setParams(param);
        Result<JSONObject> result = tuShareFeignClient.fetchData(request);
        JSONObject data = result.getData();
        JSONArray items = data.getJSONArray("items");

        List<FetchDailyLineResult> results = new ArrayList<>();
        for (int i = 0; i < items.size(); i++) {
            List list = items.getObject(i, List.class);
            FetchDailyLineResult result1 = new FetchDailyLineResult();
            LocalDate yyyymmdd = DateUtil.parseDate((String) list.get(1), "yyyyMMdd");
            result1.setDate(DateUtil.formatDate(yyyymmdd));
            String tsCode = (String) list.get(0);
            String s = tsCode.replaceAll(".SZ", "").replaceAll(".SH", "").replaceAll(".BJ", "");
            if (MarketPlate.getMarketPlate(s) == MarketPlate.HSZB) {
                result1.setCode(s);
                result1.setOpen((Double) list.get(2));
                result1.setHigh((Double) list.get(3));
                result1.setLow((Double) list.get(4));
                result1.setPrice((Double) list.get(5));
                result1.setUpValue((Double) list.get(7));
                result1.setUpPercentage(NumberUtils.format((Double) list.get(8)));
                result1.setVolume((Double) list.get(9));
                result1.setAmount((Double) list.get(10) * 1000D);
                results.add(result1);
            }
        }
        return results;
    }
}
